{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "GEN_5_Pix2Pix.ipynb",
      "provenance": [],
      "collapsed_sections": [],
      "include_colab_link": true
    },
    "kernelspec": {
      "name": "python3",
      "display_name": "Python 3"
    },
    "accelerator": "GPU"
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "view-in-github",
        "colab_type": "text"
      },
      "source": [
        "<a href=\"https://colab.research.google.com/github/cxbxmxcx/GenReality/blob/master/GEN_5_Pix2Pix.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "jcOwebqPwKux"
      },
      "source": [
        "#@title IMPORTS \n",
        "#@markdown run to import modules\n",
        "import argparse\n",
        "import os\n",
        "import numpy as np\n",
        "import math\n",
        "import itertools\n",
        "import scipy\n",
        "import sys\n",
        "import time\n",
        "import datetime\n",
        "\n",
        "import torchvision.transforms as transforms\n",
        "from torchvision.utils import save_image\n",
        "\n",
        "from torch.utils.data import DataLoader\n",
        "from torchvision import datasets\n",
        "from torch.autograd import Variable\n",
        "import torch.autograd as autograd\n",
        "from torchvision.utils import make_grid\n",
        "\n",
        "import torch.nn as nn\n",
        "import torch.nn.functional as F\n",
        "import torch\n",
        "\n",
        "import matplotlib.pyplot as plt\n",
        "from matplotlib.pyplot import figure\n",
        "from IPython.display import clear_output"
      ],
      "execution_count": 14,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "rVBTY_4wwfbq",
        "outputId": "5b9a543a-6e6a-4d9e-8f17-d11d62a7e14c"
      },
      "source": [
        "#@title HYPERPARAMETERS \n",
        "dataset_name = \"maps\" #@param [\"facades\", \"cityscapes\", \"maps\"]\n",
        "class Hyperparameters(object):\n",
        "  def __init__(self, **kwargs):\n",
        "    self.__dict__.update(kwargs)\n",
        "\n",
        "datasets = { \"facades\" : { \"url\" : 'https://www.dropbox.com/s/0gnznt9m480rps7/facades.zip?dl=1',\n",
        "                          \"train_mode\" : \"train\",\n",
        "                          \"test_mode\" : \"test\"},\n",
        "            \"cityscapes\" : { \"url\" : \"https://www.dropbox.com/s/2a4v13fd45zwdg8/cityscapes.zip?dl=1\",\n",
        "                            \"train_mode\" : \"train\",\n",
        "                            \"test_mode\" : \"val\"},\n",
        "            \"maps\" : { \"url\" : \"https://www.dropbox.com/s/ut7g1r73cq5zdrn/maps.zip?dl=1\",\n",
        "                      \"train_mode\" : \"train\",\n",
        "                      \"test_mode\" : \"val\"}\n",
        "            }\n",
        "\n",
        "hp = Hyperparameters(\n",
        "    epoch=0,\n",
        "    n_epochs=200,\n",
        "    batch_size=1,    \n",
        "    dataset_name=dataset_name,\n",
        "    dataset_url=datasets[dataset_name][\"url\"],\n",
        "    dataset_train_mode=datasets[dataset_name][\"train_mode\"],\n",
        "    dataset_test_mode=datasets[dataset_name][\"test_mode\"],    \n",
        "    lr=.0002,\n",
        "    b1=.5,\n",
        "    b2=0.999,\n",
        "    n_cpu=8,\n",
        "    img_size=256,\n",
        "    channels=3,\n",
        "    n_critic=5,\n",
        "    sample_interval=100,\n",
        "    lambda_pixel=100)\n",
        "\n",
        "img_root_folder = 'images'\n",
        "os.makedirs(img_root_folder, exist_ok=True)\n",
        "image_folder = img_root_folder + \"/%s\" % hp.dataset_name \n",
        "print(f\"Image data folders constructed {image_folder}\")\n",
        "os.makedirs(image_folder, exist_ok=True)"
      ],
      "execution_count": 15,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Image data folders constructed images/maps\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "Jzze0ldCx1O_",
        "cellView": "form",
        "outputId": "b97ab8d2-147f-4d8c-9462-df8581344e59"
      },
      "source": [
        "#@title DOWNLOAD DATASET\n",
        "from io import BytesIO\n",
        "from urllib.request import urlopen\n",
        "from zipfile import ZipFile\n",
        "zipurl = hp.dataset_url\n",
        "with urlopen(zipurl) as zipresp:\n",
        "    with ZipFile(BytesIO(zipresp.read())) as zfile:        \n",
        "        zfile.extractall(img_root_folder)\n",
        "        print(f\"Downloaded & Extracted {zipurl}\")"
      ],
      "execution_count": 16,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Downloaded & Extracted https://www.dropbox.com/s/ut7g1r73cq5zdrn/maps.zip?dl=1\n"
          ],
          "name": "stdout"
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "yZaSsdJa_xqH",
        "cellView": "form"
      },
      "source": [
        "#@title IMAGE DOWNLOAD HELPERS AND FORATTERS\n",
        "\n",
        "import glob\n",
        "import random\n",
        "import os\n",
        "from torch.utils.data import Dataset\n",
        "from PIL import Image\n",
        "\n",
        "class ImageDataset(Dataset):\n",
        "    def __init__(self, root, transforms_=None, mode=\"train\"):\n",
        "        self.transform = transforms.Compose(transforms_)\n",
        "\n",
        "        self.files = sorted(glob.glob(os.path.join(root, mode) + \"/*.*\"))\n",
        "        if mode == \"train\":\n",
        "            self.files.extend(sorted(glob.glob(os.path.join(root, \"test\") + \"/*.*\")))\n",
        "\n",
        "    def __getitem__(self, index):\n",
        "\n",
        "        img = Image.open(self.files[index % len(self.files)])\n",
        "        w, h = img.size\n",
        "        img_A = img.crop((0, 0, w / 2, h))\n",
        "        img_B = img.crop((w / 2, 0, w, h))\n",
        "\n",
        "        if np.random.random() < 0.5:\n",
        "            img_A = Image.fromarray(np.array(img_A)[:, ::-1, :], \"RGB\")\n",
        "            img_B = Image.fromarray(np.array(img_B)[:, ::-1, :], \"RGB\")\n",
        "\n",
        "        img_A = self.transform(img_A)\n",
        "        img_B = self.transform(img_B)\n",
        "\n",
        "        return {\"A\": img_A, \"B\": img_B}\n",
        "\n",
        "    def __len__(self):\n",
        "        return len(self.files)"
      ],
      "execution_count": 17,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "VWcpYFFnWAbv",
        "cellView": "form"
      },
      "source": [
        "#@title IMAGE VISUALIZER HELPERS\n",
        "def imshow(img,size=10):\n",
        "  img = img / 2 + 0.5     \n",
        "  npimg = img.numpy()\n",
        "  plt.figure(figsize=(size, size))\n",
        "  plt.imshow(np.transpose(npimg, (1, 2, 0)))\n",
        "  plt.show()\n",
        "\n",
        "def to_img(x):    \n",
        "    x = x.view(x.size(0)*2, hp.channels, hp.img_size, hp.img_size)\n",
        "    return x\n",
        "\n",
        "import matplotlib.image as mpimg\n",
        "def visualise_output(path, x, y):\n",
        "    img = mpimg.imread(path)\n",
        "    plt.figure(figsize=(x,y))\n",
        "    plt.imshow(img)  \n",
        "    plt.show()"
      ],
      "execution_count": 18,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "pR5LjEFQARFm",
        "cellView": "form"
      },
      "source": [
        "#@title CONFIGURE DATALOADERS\n",
        "transforms_ = [\n",
        "    transforms.Resize((hp.img_size, hp.img_size), Image.BICUBIC),\n",
        "    transforms.ToTensor(),\n",
        "    transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),\n",
        "]\n",
        "\n",
        "dataloader = DataLoader(\n",
        "    ImageDataset(image_folder, mode=hp.dataset_train_mode, transforms_=transforms_),\n",
        "    batch_size=hp.batch_size,\n",
        "    shuffle=True,\n",
        "    num_workers=1,\n",
        ")\n",
        "val_dataloader = DataLoader(\n",
        "    ImageDataset(image_folder, mode=hp.dataset_test_mode, transforms_=transforms_),\n",
        "    batch_size=16,\n",
        "    shuffle=True,\n",
        "    num_workers=1,\n",
        ")"
      ],
      "execution_count": 19,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 108
        },
        "id": "HbxCwP0zXyBQ",
        "outputId": "0a4299d9-4e0d-4ac9-b04a-c37bd992a15e"
      },
      "source": [
        "#@title VISUALING SAMPLE DATA { run: \"auto\" }\n",
        "pic_size = 2 #@param {type:\"integer\"} {type:\"slider\", min:1, max:30, step:1}\n",
        "\n",
        "dataiter = iter(dataloader)\n",
        "images = dataiter.next()\n",
        "\n",
        "for i in range(len(images[\"A\"])):\n",
        "  imshow(make_grid([images[\"A\"][i],images[\"B\"][i]]), size=pic_size)"
      ],
      "execution_count": 20,
      "outputs": [
        {
          "output_type": "display_data",
          "data": {
            "image/png": "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\n",
            "text/plain": [
              "<Figure size 144x144 with 1 Axes>"
            ]
          },
          "metadata": {
            "tags": [],
            "needs_background": "light"
          }
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "EEb5TdBmIy7l",
        "cellView": "form"
      },
      "source": [
        "#@title GENERATOR & DISCRIMINATOR\n",
        "def weights_init_normal(m):\n",
        "    classname = m.__class__.__name__\n",
        "    if classname.find(\"Conv\") != -1:\n",
        "        torch.nn.init.normal_(m.weight.data, 0.0, 0.02)\n",
        "    elif classname.find(\"BatchNorm2d\") != -1:\n",
        "        torch.nn.init.normal_(m.weight.data, 1.0, 0.02)\n",
        "        torch.nn.init.constant_(m.bias.data, 0.0)\n",
        "\n",
        "\n",
        "##############################\n",
        "#           U-NET\n",
        "##############################\n",
        "\n",
        "\n",
        "class UNetDown(nn.Module):\n",
        "    def __init__(self, in_size, out_size, normalize=True, dropout=0.0):\n",
        "        super(UNetDown, self).__init__()\n",
        "        layers = [nn.Conv2d(in_size, out_size, 4, 2, 1, bias=False)]\n",
        "        if normalize:\n",
        "            layers.append(nn.InstanceNorm2d(out_size))\n",
        "        layers.append(nn.LeakyReLU(0.2))\n",
        "        if dropout:\n",
        "            layers.append(nn.Dropout(dropout))\n",
        "        self.model = nn.Sequential(*layers)\n",
        "\n",
        "    def forward(self, x):\n",
        "        return self.model(x)\n",
        "\n",
        "\n",
        "class UNetUp(nn.Module):\n",
        "    def __init__(self, in_size, out_size, dropout=0.0):\n",
        "        super(UNetUp, self).__init__()\n",
        "        layers = [\n",
        "            nn.ConvTranspose2d(in_size, out_size, 4, 2, 1, bias=False),\n",
        "            nn.InstanceNorm2d(out_size),\n",
        "            nn.ReLU(inplace=True),\n",
        "        ]\n",
        "        if dropout:\n",
        "            layers.append(nn.Dropout(dropout))\n",
        "\n",
        "        self.model = nn.Sequential(*layers)\n",
        "\n",
        "    def forward(self, x, skip_input):\n",
        "        x = self.model(x)\n",
        "        x = torch.cat((x, skip_input), 1)\n",
        "\n",
        "        return x\n",
        "\n",
        "\n",
        "class GeneratorUNet(nn.Module):\n",
        "    def __init__(self, in_channels=3, out_channels=3):\n",
        "        super(GeneratorUNet, self).__init__()\n",
        "\n",
        "        self.down1 = UNetDown(in_channels, 64, normalize=False)\n",
        "        self.down2 = UNetDown(64, 128)\n",
        "        self.down3 = UNetDown(128, 256)\n",
        "        self.down4 = UNetDown(256, 512, dropout=0.5)\n",
        "        self.down5 = UNetDown(512, 512, dropout=0.5)\n",
        "        self.down6 = UNetDown(512, 512, dropout=0.5)\n",
        "        self.down7 = UNetDown(512, 512, dropout=0.5)\n",
        "        self.down8 = UNetDown(512, 512, normalize=False, dropout=0.5)\n",
        "\n",
        "        self.up1 = UNetUp(512, 512, dropout=0.5)\n",
        "        self.up2 = UNetUp(1024, 512, dropout=0.5)\n",
        "        self.up3 = UNetUp(1024, 512, dropout=0.5)\n",
        "        self.up4 = UNetUp(1024, 512, dropout=0.5)\n",
        "        self.up5 = UNetUp(1024, 256)\n",
        "        self.up6 = UNetUp(512, 128)\n",
        "        self.up7 = UNetUp(256, 64)\n",
        "\n",
        "        self.final = nn.Sequential(\n",
        "            nn.Upsample(scale_factor=2),\n",
        "            nn.ZeroPad2d((1, 0, 1, 0)),\n",
        "            nn.Conv2d(128, out_channels, 4, padding=1),\n",
        "            nn.Tanh(),\n",
        "        )\n",
        "\n",
        "    def forward(self, x):\n",
        "        # U-Net generator with skip connections from encoder to decoder\n",
        "        d1 = self.down1(x)\n",
        "        d2 = self.down2(d1)\n",
        "        d3 = self.down3(d2)\n",
        "        d4 = self.down4(d3)\n",
        "        d5 = self.down5(d4)\n",
        "        d6 = self.down6(d5)\n",
        "        d7 = self.down7(d6)\n",
        "        d8 = self.down8(d7)\n",
        "        u1 = self.up1(d8, d7)\n",
        "        u2 = self.up2(u1, d6)\n",
        "        u3 = self.up3(u2, d5)\n",
        "        u4 = self.up4(u3, d4)\n",
        "        u5 = self.up5(u4, d3)\n",
        "        u6 = self.up6(u5, d2)\n",
        "        u7 = self.up7(u6, d1)\n",
        "\n",
        "        return self.final(u7)\n",
        "\n",
        "\n",
        "##############################\n",
        "#        Discriminator\n",
        "##############################\n",
        "\n",
        "\n",
        "class Discriminator(nn.Module):\n",
        "    def __init__(self, in_channels=3):\n",
        "        super(Discriminator, self).__init__()\n",
        "\n",
        "        def discriminator_block(in_filters, out_filters, normalization=True):\n",
        "            \"\"\"Returns downsampling layers of each discriminator block\"\"\"\n",
        "            layers = [nn.Conv2d(in_filters, out_filters, 4, stride=2, padding=1)]\n",
        "            if normalization:\n",
        "                layers.append(nn.InstanceNorm2d(out_filters))\n",
        "            layers.append(nn.LeakyReLU(0.2, inplace=True))\n",
        "            return layers\n",
        "\n",
        "        self.model = nn.Sequential(\n",
        "            *discriminator_block(in_channels * 2, 64, normalization=False),\n",
        "            *discriminator_block(64, 128),\n",
        "            *discriminator_block(128, 256),\n",
        "            *discriminator_block(256, 512),\n",
        "            nn.ZeroPad2d((1, 0, 1, 0)),\n",
        "            nn.Conv2d(512, 1, 4, padding=1, bias=False)\n",
        "        )\n",
        "\n",
        "    def forward(self, img_A, img_B):\n",
        "        # Concatenate image and condition image by channels to produce input\n",
        "        img_input = torch.cat((img_A, img_B), 1)\n",
        "        return self.model(img_input)"
      ],
      "execution_count": 21,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/"
        },
        "id": "YxFuX3PCKOVW",
        "outputId": "bfabb883-910a-48fe-a63f-25d70b12f186"
      },
      "source": [
        "#@title SETUP, LOSS and INITIALIZE MODELS\n",
        "\n",
        "cuda = True if torch.cuda.is_available() else False\n",
        "print(\"Using CUDA\" if cuda else \"Not using CUDA\")\n",
        "\n",
        "# Loss functions\n",
        "criterion_GAN = torch.nn.MSELoss()\n",
        "criterion_pixelwise = torch.nn.L1Loss()\n",
        "\n",
        "# Calculate output of image discriminator (PatchGAN)\n",
        "patch = (1, hp.img_size // 2 ** 4, hp.img_size // 2 ** 4)\n",
        "\n",
        "# Initialize generator and discriminator\n",
        "generator = GeneratorUNet()\n",
        "discriminator = Discriminator()\n",
        "\n",
        "if cuda:\n",
        "    generator = generator.cuda()\n",
        "    discriminator = discriminator.cuda()\n",
        "    criterion_GAN.cuda()\n",
        "    criterion_pixelwise.cuda()\n",
        "\n",
        "# Initialize weights\n",
        "generator.apply(weights_init_normal)\n",
        "discriminator.apply(weights_init_normal)"
      ],
      "execution_count": 22,
      "outputs": [
        {
          "output_type": "stream",
          "text": [
            "Using CUDA\n"
          ],
          "name": "stdout"
        },
        {
          "output_type": "execute_result",
          "data": {
            "text/plain": [
              "Discriminator(\n",
              "  (model): Sequential(\n",
              "    (0): Conv2d(6, 64, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))\n",
              "    (1): LeakyReLU(negative_slope=0.2, inplace=True)\n",
              "    (2): Conv2d(64, 128, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))\n",
              "    (3): InstanceNorm2d(128, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)\n",
              "    (4): LeakyReLU(negative_slope=0.2, inplace=True)\n",
              "    (5): Conv2d(128, 256, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))\n",
              "    (6): InstanceNorm2d(256, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)\n",
              "    (7): LeakyReLU(negative_slope=0.2, inplace=True)\n",
              "    (8): Conv2d(256, 512, kernel_size=(4, 4), stride=(2, 2), padding=(1, 1))\n",
              "    (9): InstanceNorm2d(512, eps=1e-05, momentum=0.1, affine=False, track_running_stats=False)\n",
              "    (10): LeakyReLU(negative_slope=0.2, inplace=True)\n",
              "    (11): ZeroPad2d(padding=(1, 0, 1, 0), value=0.0)\n",
              "    (12): Conv2d(512, 1, kernel_size=(4, 4), stride=(1, 1), padding=(1, 1), bias=False)\n",
              "  )\n",
              ")"
            ]
          },
          "metadata": {
            "tags": []
          },
          "execution_count": 22
        }
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "TpEIFH7BIRwA"
      },
      "source": [
        "#@title SAMPLING IMAGES\n",
        "def sample_images(batches_done):\n",
        "    \"\"\"Saves a generated sample from the validation set\"\"\"\n",
        "    imgs = next(iter(val_dataloader))\n",
        "    real_A = Variable(imgs[\"B\"].type(Tensor))\n",
        "    real_B = Variable(imgs[\"A\"].type(Tensor))\n",
        "    fake_B = generator(real_A)\n",
        "    img_sample = torch.cat((real_A.data, fake_B.data, real_B.data), -2)\n",
        "    path = \"images/%s/%s.png\" % (hp.dataset_name, batches_done)\n",
        "    save_image(img_sample, path, nrow=8, normalize=True) \n",
        "    return path\n",
        "    "
      ],
      "execution_count": 23,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "id": "fgRoX2DbHSwk"
      },
      "source": [
        "#@title OPTIMIZERS\n",
        "optimizer_G = torch.optim.Adam(generator.parameters(), lr=hp.lr, betas=(hp.b1, hp.b2))\n",
        "optimizer_D = torch.optim.Adam(discriminator.parameters(), lr=hp.lr, betas=(hp.b1, hp.b2))\n",
        "\n",
        "FloatTensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor\n",
        "LongTensor = torch.cuda.LongTensor if cuda else torch.LongTensor\n",
        "Tensor = torch.cuda.FloatTensor if cuda else torch.FloatTensor"
      ],
      "execution_count": 24,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab": {
          "base_uri": "https://localhost:8080/",
          "height": 438
        },
        "id": "GiWH4SwoI7jA",
        "outputId": "40dcbdf5-1ea2-4381-aa0e-db495a2ded0f"
      },
      "source": [
        "#@title TRAINING\n",
        "# ----------\n",
        "#  Training\n",
        "# ----------\n",
        "\n",
        "prev_time = time.time()\n",
        "\n",
        "for epoch in range(hp.epoch, hp.n_epochs):\n",
        "    for i, batch in enumerate(dataloader):\n",
        "\n",
        "        # Model inputs\n",
        "        real_A = Variable(batch[\"B\"].type(Tensor))\n",
        "        real_B = Variable(batch[\"A\"].type(Tensor))\n",
        "\n",
        "        # Adversarial ground truths\n",
        "        valid = Variable(Tensor(np.ones((real_A.size(0), *patch))), requires_grad=False)\n",
        "        fake = Variable(Tensor(np.zeros((real_A.size(0), *patch))), requires_grad=False)\n",
        "\n",
        "        # ------------------\n",
        "        #  Train Generators\n",
        "        # ------------------\n",
        "\n",
        "        optimizer_G.zero_grad()\n",
        "\n",
        "        # GAN loss\n",
        "        fake_B = generator(real_A)\n",
        "        pred_fake = discriminator(fake_B, real_A)\n",
        "        loss_GAN = criterion_GAN(pred_fake, valid)\n",
        "        # Pixel-wise loss\n",
        "        loss_pixel = criterion_pixelwise(fake_B, real_B)\n",
        "\n",
        "        # Total loss\n",
        "        loss_G = loss_GAN + hp.lambda_pixel * loss_pixel\n",
        "\n",
        "        loss_G.backward()\n",
        "\n",
        "        optimizer_G.step()\n",
        "\n",
        "        # ---------------------\n",
        "        #  Train Discriminator\n",
        "        # ---------------------\n",
        "\n",
        "        optimizer_D.zero_grad()\n",
        "\n",
        "        # Real loss\n",
        "        pred_real = discriminator(real_B, real_A)\n",
        "        loss_real = criterion_GAN(pred_real, valid)\n",
        "\n",
        "        # Fake loss\n",
        "        pred_fake = discriminator(fake_B.detach(), real_A)\n",
        "        loss_fake = criterion_GAN(pred_fake, fake)\n",
        "\n",
        "        # Total loss\n",
        "        loss_D = 0.5 * (loss_real + loss_fake)\n",
        "\n",
        "        loss_D.backward()\n",
        "        optimizer_D.step()\n",
        "\n",
        "        # --------------\n",
        "        #  Log Progress\n",
        "        # --------------\n",
        "\n",
        "        # Determine approximate time left\n",
        "        batches_done = epoch * len(dataloader) + i\n",
        "        batches_left = hp.n_epochs * len(dataloader) - batches_done\n",
        "        time_left = datetime.timedelta(seconds=batches_left * (time.time() - prev_time))\n",
        "        prev_time = time.time()\n",
        "\n",
        "        # Print log\n",
        "        sys.stdout.write(\n",
        "            \"\\r[Epoch %d/%d] [Batch %d/%d] [D loss: %f] [G loss: %f, pixel: %f, adv: %f] ETA: %s\"\n",
        "            % (\n",
        "                epoch,\n",
        "                hp.n_epochs,\n",
        "                i,\n",
        "                len(dataloader),\n",
        "                loss_D.item(),\n",
        "                loss_G.item(),\n",
        "                loss_pixel.item(),\n",
        "                loss_GAN.item(),\n",
        "                time_left,\n",
        "            )\n",
        "        )\n",
        "\n",
        "        # If at sample interval save image\n",
        "        if batches_done % hp.sample_interval == 0:\n",
        "          clear_output()\n",
        "          visualise_output(sample_images(batches_done), 10, 10)\n",
        "\n",
        " "
      ],
      "execution_count": 25,
      "outputs": [
        {
          "output_type": "error",
          "ename": "KeyboardInterrupt",
          "evalue": "ignored",
          "traceback": [
            "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
            "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
            "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/PIL/ImageFile.py\u001b[0m in \u001b[0;36m_save\u001b[0;34m(im, fp, tile, bufsize)\u001b[0m\n\u001b[1;32m    495\u001b[0m     \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 496\u001b[0;31m         \u001b[0mfh\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mfp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfileno\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    497\u001b[0m         \u001b[0mfp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mflush\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;31mAttributeError\u001b[0m: '_idat' object has no attribute 'fileno'",
            "\nDuring handling of the above exception, another exception occurred:\n",
            "\u001b[0;31mKeyboardInterrupt\u001b[0m                         Traceback (most recent call last)",
            "\u001b[0;32m<ipython-input-25-2fe21786c5e7>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     86\u001b[0m         \u001b[0;32mif\u001b[0m \u001b[0mbatches_done\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0mhp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msample_interval\u001b[0m \u001b[0;34m==\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     87\u001b[0m           \u001b[0mclear_output\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 88\u001b[0;31m           \u001b[0mvisualise_output\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0msample_images\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbatches_done\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m10\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     89\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     90\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m<ipython-input-23-2bf03013489f>\u001b[0m in \u001b[0;36msample_images\u001b[0;34m(batches_done)\u001b[0m\n\u001b[1;32m      8\u001b[0m     \u001b[0mimg_sample\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mreal_A\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfake_B\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mreal_B\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdata\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m-\u001b[0m\u001b[0;36m2\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m      9\u001b[0m     \u001b[0mpath\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;34m\"images/%s/%s.png\"\u001b[0m \u001b[0;34m%\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mhp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mdataset_name\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mbatches_done\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 10\u001b[0;31m     \u001b[0msave_image\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mimg_sample\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnrow\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;36m8\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mnormalize\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;32mTrue\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m     11\u001b[0m     \u001b[0;32mreturn\u001b[0m \u001b[0mpath\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     12\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/torchvision/utils.py\u001b[0m in \u001b[0;36msave_image\u001b[0;34m(tensor, fp, nrow, padding, normalize, range, scale_each, pad_value, format)\u001b[0m\n\u001b[1;32m    128\u001b[0m     \u001b[0mndarr\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mgrid\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mmul\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m255\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0madd_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0.5\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mclamp_\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m255\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpermute\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m1\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m2\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mto\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m'cpu'\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mtorch\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0muint8\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mnumpy\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    129\u001b[0m     \u001b[0mim\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0mImage\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfromarray\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mndarr\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 130\u001b[0;31m     \u001b[0mim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msave\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mformat\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0mformat\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
            "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/PIL/Image.py\u001b[0m in \u001b[0;36msave\u001b[0;34m(self, fp, format, **params)\u001b[0m\n\u001b[1;32m   2100\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   2101\u001b[0m         \u001b[0;32mtry\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m-> 2102\u001b[0;31m             \u001b[0msave_handler\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mself\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mfilename\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m   2103\u001b[0m         \u001b[0;32mfinally\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m   2104\u001b[0m             \u001b[0;31m# do what we can to clean up\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/PIL/PngImagePlugin.py\u001b[0m in \u001b[0;36m_save\u001b[0;34m(im, fp, filename, chunk)\u001b[0m\n\u001b[1;32m    898\u001b[0m         \u001b[0mchunk\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34mb\"eXIf\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mexif\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    899\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 900\u001b[0;31m     \u001b[0mImageFile\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m_save\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mim\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0m_idat\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mchunk\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m[\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"zip\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m)\u001b[0m \u001b[0;34m+\u001b[0m \u001b[0mim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0msize\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;36m0\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mrawmode\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m]\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    901\u001b[0m \u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    902\u001b[0m     \u001b[0mchunk\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mfp\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34mb\"IEND\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34mb\"\"\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;32m/usr/local/lib/python3.6/dist-packages/PIL/ImageFile.py\u001b[0m in \u001b[0;36m_save\u001b[0;34m(im, fp, tile, bufsize)\u001b[0m\n\u001b[1;32m    508\u001b[0m             \u001b[0;32melse\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    509\u001b[0m                 \u001b[0;32mwhile\u001b[0m \u001b[0;32mTrue\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m--> 510\u001b[0;31m                     \u001b[0ml\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0md\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0me\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mencode\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0mbufsize\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m\u001b[1;32m    511\u001b[0m                     \u001b[0mfp\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwrite\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0md\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m    512\u001b[0m                     \u001b[0;32mif\u001b[0m \u001b[0ms\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n",
            "\u001b[0;31mKeyboardInterrupt\u001b[0m: "
          ]
        }
      ]
    }
  ]
}